Keyboard Sleuth
Volume Number: 2
Issue Number: 8
Column Tag: Resource Roundup
Be a Keyboard Sleuth! 
By Joel West, Contributing Editor, Vista, CA
See the world through a keyboard
In the last installment of Resource Roundup, the general concepts of resources
were introduced, and passing mention was made of their role in Apple’s international
marketing.
Among the stated justfications for the resource concept was to make it easier to
support software in multiple countries. Most software previously had strings
embedded in the software, such as
write('File ', filenam,
' cannot be opened.')
which have to be completely rewritten by a programmer (using the proprietary
source code) for any foreign market. However, with resources available, a properly
designed Macintosh application can be translated by just about anybody using REdit. (I
don’t know Steve Jobs personally, so I can’t say if this was a major factor, or merely
an after-the-fact rationalization for the design.)
If you’ve ever studied a foreign language, you’ve probably come to the shocking
realization that not all of the Indo-European languages content themselves with the
letters A-Z and standard punctuation. I can certainly recall struggling in high school
Spanish trying to approximate the key with a typewriter (the wasn’t even worth
trying) or to come up with the B in German.
If you’ve concluded by this introduction that resources are used on the Macintosh
to support foreign keyboards, you’re right. But these resources are also used to
support the differences between the Macintosh and the Macintosh Plus keyboards, as
well as implementing basic keyboard functions. There are also some important
electronic and mechanical differences between the various keyboards.
Smart, but not smart enough
If you ask Apple how to read input from the keyboard, you’ll get the response
that you shouldn’t use anything other than the ASCII value. Apple has gone to great
lengths to make various keyboards and nationalities behave similarly.
However, the return values from GetNextEvent (and other less documented
interfaces) do include the actual key number that is down. You can use this value to
support key combinations that are not defined by Apple -- as long as you are aware of
keyboard differences.
There are a number of reasons why you might need to go to these lower levels.
My interest in keyboards was prompted by the terminal program I’ve been working on
in my spare time (at the rate I’m going, it should be ready in 1993).
A number of otherwise good Macintosh programs have tried to reach this low
level and failed. My ears first picked up when I heard a number of terminal emulation
programs don’t work with non-US keyboards, or with the Mac Plus. The problem hasn’t
even escaped Apple; an early version of their Smalltalk-80 is one such program, as
we’ll see later.
Output of the Keyboard Sleuth!
But there are ways to look at lower levels that are compatible with all current
systems and should be compatible with all future systems. This article tells how you
can take complete control over the keyboard without sacrificing portability. It
describes the various un- and semi-documented resources associated with keyboards,
including the Macintosh Plus and those sold outside the US.
This article concludes with “Keyboard Sleuth”, a program example that
analyzes and reports what keyboard you’re using, and which keys you are pressing. The
program was written in 'Rascal', a Pascal type language from Reed College. This
language is a combination Pascal, Basic and Assembly. It has a very nice shell that
makes Mac programming fast and easy because it allows you more flexibility for dealing
with the various toolbox data structures than the traditionally strongly typed Pascal.
This makes it more suited to "quicky" programs where the Mac interface is secondary to
the programming problem. (The distributor for Rascal is given at the end of this
article.) The complete Rascal version is available on the source code disk for this issue.
It should be fairly easy to translate the program into TML Pascal, and in fact, our
Editor took me up on that challenge and did just that! Since many of MacTutor's readers
probably have not heard of Rascal, we are publishing his TML version of the program
with this article (see Sleuth, Part II in the next article).
Keyboard Properties
The best place to start on keyboard input is the “Toolbox Event Manager”
chapter of Inside Macintosh, if you haven’t read it already. There’s also a brief
discussion in the “Macintosh Hardware” chapter, but ignore the key numbers shown in
Figure 9 of that chapter. My interest is software, not hardware, so I’ll focus on the
former in this article.
For many purposes, the keyboard resembles a standard teletype (TTY) input.
When you type on the keyboard, a series of ASCII characters are available for you.
If you want to dig deeper (and if you didn’t, you wouldn’t subscribe to
MacTutor), there are a number of differences from a glass TTY:
• Extended characters. Character values 0 to 127 are defined by the ASCII
standard. For the Lisa and Macintosh, Apple has added a number of characters
(128 to 216, thus far) to support foreign languages and typographic symbols.
I’ll refer to these as extended ASCII characters. For simplicity’s sake, I’ll use
ASCII to refer to any code in the range 0 to 216, and standard ASCII for those in
the range 0 to 127.
• Key codes. In addition to the ASCII numeric value, the actual key number is
available. For each keyboard and nationality, Apple defines a standard mapping of
modifiers and key codes to ASCII characters, but for some purposes (e.g., a
terminal emulator) you may wish to use a different mapping.
• Modifier keys. The movement of certain keys is not normally available to your
program. Instead, these keys modify the values returned by other, primary keys.
These modifier keys are the Option, Command (), Shift and Caps Lock. A key code
and a particular modifer pattern will (usually) determine the ASCII value your
program sees.
• Function Keys. Apple has reserved certain special key combinations to invoke
general-utility memory resident programs. These all take the form of
Command-Shift-digit, of which only four are currently assigned. You may wish
to disable these keys for some purposes. For example, cmd-shift-3 will capture
the screen and save it to disk in a paint document. These are known as 'FKEYS' or
function keys. You can create and install your own function key routines in the
system file in a manner similar to writing desk accessories.
• “Dead” keys. In most cases, the accented letters used in French, Spanish,
German and Italian are produced by first typing the accent, then typing the letter.
Your program won’t normally see the first key; instead, the ASCII value of the
two-key combination is returned. The first accent key is considered “dead”
because it doesn’t return a separate ASCII value or key code.
• Different Keyboards. The Mac 128/512 and the Mac Plus have slightly different
keyboard configurations. In addition, Apple has defined a whole family of
keyboards for various countries and languages.
The resources and global variables used to implement these properties are listed
in Fig. 1.
Figure 1: Keyboard-related resource and global variables
Character Set
The standard ASCII/ISO character set defines 95 printable characters (including
space), which are directly supported by the Mac. There are also nine non-printing
characters which can be typed, as shown with their corresponding hex codes in Figure
2. Backspace, Tab, and Return have meanings similar to their accepted ASCII usages,
while Apple has adopted arbitrary ASCII values for the other six keys.
If you are echoing input characters to the screen, you will have to interpret
these control characters yourself. Although TextEdit understands the Return key, in
general, these keys won’t produce any meaningful display when using standard output
routines, such as DrawString or TEUpdate.
Additionally, there are the 89 extended ASCII characters, as shown in Figure 3.
A few support mathematical and word processing symbols, such as the copyright (©)
and paragraph (¶) symbols.
However, most of these are used to support foreign languages and typography. A
number of languages have extended alphabets. These include accented letters (such as á,
à, â, ä, ã, å), combinations (æ, ) and as well as characters that do not have English
equivalents (B).
In addition, each language has its own typographic customs. In Spanish,
exclamatories and interrogatories require a leading punction mark, as in
Que? Hola!
The German language prints quotations as
Deises ist ein Zitat.
Ironically, the extended set also includes American style quotation marks, as in
“This is a quotation.”
since the quote mark (") is strictly a typewriter convenience that does not extended
to publishing.
All of the ASCII values in the range 32 to 216 (except for non-printing standard
ASCII value 127, DEL) have corresponding characters in at least one font. (The various
font-related resources will be discussed in depth in a future article.)
When a key is pressed, it generates a keyboard event. Normally, you will only
receive a keyboard event when the key goes down, with the GetNextEvent function
returning the EventRecord.what field set to keyDown. Unless you specifically enable
keyDown events, such as with
SetEventMask(everyEvent)
GetNextEvent will not return keyUp events.
You will, however, get autoKey events by default. These are generated by the
system after the key has been down for a specified delay, and are repeated
automatically.
If the event mask you pass to GetNextEvent includes keyDownMask, it should also
include autoKeyMask. If not, the consequences are hilarious, as I discovered in writing
a desk accessory in which I decided not to bother with the repeating key case. The first
letter goes to your program, and any subsequent letter will go to some other program
or desk accessory.
The delay and repeat rate can be changed by the user with the “Control Panel”
desk accessory, and are stored in global variables KeyThresh and KeyRepThresh,
respectively, in units of ticks (1/60 of a second).
The values set by the user are also saved in the non-volatile parameter
RAM,which are then used to initialize KeyThresh and KeyRepThresh. These permanent
values can be accessed by the following fragment:
{1}
CONST
aKeyRate = 8;
aKeyThresh = 12;
VAR
sysparm: SysParmType;
num: LongInt;
...
BEGIN
sysparm := GetSysPPtr;
num := ORD4(sysparm^.kbdPrint);
rate := BitAnd(BitShift(num,aKeyRate),
ORD4(15))*2;
thresh := BitAnd(BitShift(num,
aKeyThresh), ORD4(15))*4;
(The value sysparm^.kbdPrint is also available as global variable SPKbd; this is
the preferred interface for assembly language programmers).
If you want to change the permanent value for some reason, you can modify the
value of kbdPrint (or SPKbd) and then call WriteParam to make the change permanent.
See the “Operating System Utilities” of Inside Macintosh for more details.
Four of the keys on the keyboard are not considered to generate keycodes
normally, but instead act as modifiers. These are the Shift, Caps Lock, Option, and
Command key. The state of first three keys are mapped -- along with the key struck --
to generate an ASCII value.
The Command key is always a modifier, and never affects the ASCII value. On the
US keyboard, there are six modifier combinations that affect the ASCII mapping:
(none)
Caps Lock
Shift
Option
Caps Lock-Option
Shift-Option
On the US keyboard, the Caps Lock is ignored if Shift is down, but there’s no
guarantee that other keyboards will behave the same way. Note, however, your
program can always detect if the Caps Lock key was down through the
EventRecord.modifiers field. For example, the screen dump function key does this to
distinguish Command-Shift-4 (print current window) from